package dev.famer.typelevel

import shapeless.{::, _}

import scala.concurrent.Future

package object playground {

  trait ReadResult[C <: Command[_]] {
    type R
    def read(text: String): R
  }
  object ReadResult {
    type Aux[C <: Command[_], R0 <: Reply] = ReadResult[C] {
      type R = R0
    }

    def apply[C <: Command[_]](implicit rr: ReadResult[C]): ReadResult[C] = rr
  }

  trait GetResult[CL <: HList] {
    type RL <: HList
    def read(list: CL): RL
  }

  object GetResult {
    type Aux[CL <: HList, RL0 <: HList] = GetResult[CL] { type RL = RL0 }

    implicit val fromHNil: Aux[HNil, HNil] = new GetResult[HNil] {
      type RL = HNil
      def read(list: HNil): RL = HNil
    }

    implicit def fromNonEmpty[HR <: Reply, HC <: Command[HR], TC <: HList](
      implicit
      rr: ReadResult.Aux[HC, HR],
      gr: GetResult[TC],
    ): Aux[HC :: TC, HR :: gr.RL] =
      new GetResult[HC :: TC] {
        type RL = HR :: gr.RL
        def read(list: HC :: TC): RL = rr.read("") :: gr.read(list.tail)
      }

  }

  trait Client {
    def multi[CL <: HList](cmdList: CL)
                          (implicit gr: GetResult[CL]): Future[gr.RL]
  }

  val client: Client = new Client {
    override def multi[CL <: HList](cmdList: CL)
                                   (implicit gr: GetResult[CL]): Future[gr.RL] = {
      Future.unit.asInstanceOf
    }
  }

  val res = client.multi(Command.INCR() :: Command.ZADD() :: HNil)
//  (
//    GetResult.fromNonEmpty(
//      Reply.INCRReply.read,
//      GetResult.fromNonEmpty(
//        Reply.ZADDReply.read,
//        GetResult.fromHNil,
//      )
//    )
//  )

}
